#include "Platform/Windows/windowsWindow.h"
#include "windowsWindow.h"
#include <set>
#include <string>
// #include "Hazel/Core/Input.h"

// #include "Hazel/Events/ApplicationEvent.h"
// #include "Hazel/Events/MouseEvent.h"
// #include "Hazel/Events/KeyEvent.h"

// #include "Hazel/Renderer/Renderer.h"

// #include "Platform/OpenGL/OpenGLContext.h"

namespace CC
{
    struct KeyValueInfo {
        int code;
        int codeUser;
        int pressed;
    };

#define MAXKEYVALUESSET (52)
    KeyValueInfo key_values_set[MAXKEYVALUESSET] = {
        {VK_LBUTTON, Key::LBUTTON, 0},
        {VK_RBUTTON, Key::RBUTTON, 0},
        {VK_CANCEL, Key::CANCEL, 0},
        {VK_MBUTTON, Key::MBUTTON, 0},
        {0x30, Key::D0, 0},
        {0x31, Key::D1, 0},
        {0x32, Key::D2, 0},
        {0x33, Key::D3, 0},
        {0x34, Key::D4, 0},
        {0x35, Key::D5, 0},
        {0x36, Key::D6, 0},
        {0x37, Key::D7, 0},
        {0x38, Key::D8, 0},
        {0x39, Key::D9, 0}, //0-9
        {0x41, Key::A, 0}, //A
        {0x42, Key::B, 0}, //B
        {0x43, Key::C, 0}, //C
        {0x44, Key::D, 0}, //D
        {0x45, Key::E, 0}, //E
        {0x46, Key::F, 0}, //F
        {0x47, Key::G, 0}, //G
        {0x48, Key::H, 0}, //H
        {0x49, Key::I, 0}, //I
        {0x4A, Key::J, 0}, //J
        {0x4B, Key::K, 0}, //K
        {0x4C, Key::L, 0}, //L
        {0x4D, Key::M, 0}, //M
        {0x4E, Key::N, 0}, //N
        {0x4F, Key::O, 0}, //O
        {0x50, Key::P, 0}, //P
        {0x51, Key::Q, 0}, //Q
        {0x52, Key::R, 0}, //R
        {0x53, Key::S, 0}, //S
        {0x54, Key::T, 0}, //T
        {0x55, Key::U, 0}, //U
        {0x56, Key::V, 0}, //V
        {0x57, Key::W, 0}, //W
        {0x58, Key::X, 0}, //X
        {0x59, Key::Y, 0}, //Y
        {0x5A, Key::Z, 0}, //Z
        {VK_ESCAPE, Key::Escape, 0}, //Escape
        {VK_NUMPAD0, Key::KP0, 0}, //NUMPAD0
        {VK_NUMPAD1, Key::KP1, 0}, //NUMPAD1
        {VK_NUMPAD2, Key::KP2, 0}, //NUMPAD2
        {VK_NUMPAD3, Key::KP3, 0}, //NUMPAD3
        {VK_NUMPAD4, Key::KP4, 0}, //NUMPAD4
        {VK_NUMPAD5, Key::KP5, 0}, //NUMPAD5
        {VK_NUMPAD6, Key::KP6, 0}, //NUMPAD6
        {VK_NUMPAD7, Key::KP7, 0}, //NUMPAD7
        {VK_NUMPAD8, Key::KP8, 0}, //NUMPAD8
        {VK_NUMPAD9, Key::KP9, 0}, //NUMPAD9
        {VK_RETURN, Key::KPEnter, 0},
    };

std::string key_value[256] = {
    "0",
    "LBUTTON",
    "RBUTTON",
    "CANCEL",
    "MBUTTON",
    "XBUTTON1",
    "XBUTTON2",
    "07",
    "BACK",
    "TAB",
    "10",
    "11",
    "CLEAR",
    "RETURN",
    "14",
    "15",
    "SHIFT",
    "CONTROL",
    "MENU",
    "PAUSE",
    "CAPITAL",
    "KANA",
    "22",
    "JUNJA",
    "FINAL",
    "KANJI",
    "26",
    "ESCAPE",
    "CONVERT",
    "NONCONVERT",
    "ACCEPT",
    "MODECHANGE",
    "SPACE",
    "PRIOR",
    "NEXT",
    "END",
    "HOME",
    "LEFT",
    "UP",
    "RIGHT",
    "DOWN",
    "SELECT",
    "PRINT",
    "EXECUTE",
    "SNAPSHOT",
    "INSERT",
    "DELETE",
    "HELP",
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "58",
    "59",
    "60",
    "61",
    "62",
    "63",
    "64",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "LWIN",
    "RWIN",
    "APPS",
    "94",
    "SLEEP",
    "NUMPAD0",
    "NUMPAD1",
    "NUMPAD2",
    "NUMPAD3",
    "NUMPAD4",
    "NUMPAD5",
    "NUMPAD6",
    "NUMPAD7",
    "NUMPAD8",
    "NUMPAD9",
    "MULTIPLY",
    "ADD",
    "SEPARATOR",
    "SUBTRACT",
    "DECIMAL",
    "DIVIDE",
    "F1",
    "F2",
    "F3",
    "F4",
    "F5",
    "F6",
    "F7",
    "F8",
    "F9",
    "F10",
    "F11",
    "F12",
    "F13",
    "F14",
    "F15",
    "F16",
    "F17",
    "F18",
    "F19",
    "F20",
    "F21",
    "F22",
    "F23",
    "F24",
    "136",
    "137",
    "138",
    "139",
    "140",
    "141",
    "142",
    "143",
    "NUMLOCK",
    "SCROLL",
    "OEM_NEC_EQUAL",
    "OEM_FJ_MASSHOU",
    "OEM_FJ_TOUROKU",
    "OEM_FJ_LOYA",
    "OEM_FJ_ROYA",
    "151",
    "152",
    "153",
    "154",
    "155",
    "156",
    "157",
    "158",
    "159",
    "LSHIFT",
    "RSHIFT",
    "LCONTROL",
    "RCONTROL",
    "LMENU",
    "RMENU",
    "BROWSER_BACK",
    "BROWSER_FORWARD",
    "BROWSER_REFRESH",
    "BROWSER_STOP",
    "BROWSER_SEARCH",
    "BROWSER_FAVORITES",
    "BROWSER_HOME",
    "VOLUME_MUTE",
    "VOLUME_DOWN",
    "VOLUME_UP",
    "MEDIA_NEXT_TRACK",
    "MEDIA_PREV_TRACK",
    "MEDIA_STOP",
    "MEDIA_PLAY_PAUSE",
    "LAUNCH_MAIL",
    "LAUNCH_MEDIA_SELECT",
    "LAUNCH_APP1",
    "LAUNCH_APP2",
    "184",
    "185",
    ";",
    "+",
    ",",
    "-",
    ".",
    "/",
    "OEM_3",
    "193",
    "194",
    "195",
    "196",
    "197",
    "198",
    "199",
    "200",
    "201",
    "202",
    "203",
    "204",
    "205",
    "206",
    "207",
    "208",
    "209",
    "210",
    "211",
    "212",
    "213",
    "214",
    "215",
    "216",
    "217",
    "218",
    "[",
    "\\",
    "]",
    "'",
    "OEM_8",
    "224",
    "OEM_AX",
    "OEM_102",
    "ICO_HELP",
    "ICO_00",
    "PROCESSKEY",
    "ICO_CLEAR",
    "PACKET",
    "232",
    "OEM_RESET",
    "OEM_JUMP",
    "OEM_PA1",
    "OEM_PA2",
    "OEM_PA3",
    "OEM_WSCTRL",
    "OEM_CUSEL",
    "OEM_ATTN",
    "OEM_FINISH",
    "OEM_COPY",
    "OEM_AUTO",
    "OEM_ENLW",
    "OEM_BACKTAB",
    "ATTN",
    "CRSEL",
    "EXSEL",
    "EREOF",
    "PLAY",
    "ZOOM",
    "NONAME",
    "PA1",
    "OEM_CLEAR"
};

std::set<std::string> input_keys = {
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "NUMPAD0",
    "NUMPAD1",
    "NUMPAD2",
    "NUMPAD3",
    "NUMPAD4",
    "NUMPAD5",
    "NUMPAD6",
    "NUMPAD7",
    "NUMPAD8",
    "NUMPAD9",
    "SPACE",
    "LEFT",
    "UP",
    "RIGHT",
    "DOWN",
    "BACK",
    "RETURN",
};
	
	static uint8_t s_GLFWWindowCount = 0;

	static void GLFWErrorCallback(int error, const char *description)
	{
		// HZ_CORE_ERROR("GLFW Error ({0}): {1}", error, description);
	}

	WindowsWindow::WindowsWindow(const WindowProps &props)
	{
		Init(props);
	}

	WindowsWindow::~WindowsWindow()
	{
		Shutdown();
	}

	void WindowsWindow::Init(const WindowProps &props)
	{
		m_Data.Title = props.Title;
		m_Data.Width = props.Width;
		m_Data.Height = props.Height;
        m_Data.IsDesktop = false;
        m_Data.Floating = props.Floating;
        m_Data.HasTitle = props.HasTitle;

		// HZ_CORE_INFO("Creating window {0} ({1}, {2})", props.Title, props.Width, props.Height);

		if (s_GLFWWindowCount == 0)
		{
			int success = glfwInit();
			glfwSetErrorCallback(GLFWErrorCallback);
		}

		{
			// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    		// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
			//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
            glfwWindowHint(GLFW_SAMPLES, 4);
            glfwWindowHint(GLFW_DECORATED, m_Data.HasTitle ? GLFW_TRUE : GLFW_FALSE);
            glfwWindowHint(GLFW_FLOATING, m_Data.Floating ? GLFW_TRUE : GLFW_FALSE);
			m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);            
			++s_GLFWWindowCount;
		}

		m_Context = GraphicsContext::Create(m_Window);
		m_Context->Init();

		glfwSetWindowUserPointer(m_Window, &m_Data);
		SetVSync(true);

		// Set GLFW callbacks
		glfwSetWindowSizeCallback(m_Window, [](GLFWwindow *window, int width, int height)
								  {
									  WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);
									  data.Width = width;
									  data.Height = height;

									  WindowResizeEvent event(width, height);
									  data.EventCallback(event);
								  });

        glfwSetFramebufferSizeCallback(m_Window, [](GLFWwindow *window, int width, int height)
            {
                WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

                WindowFramebufferResizeEvent event(width, height);
                data.EventCallback(event);
            });

		glfwSetWindowCloseCallback(m_Window, [](GLFWwindow *window)
								   {
									   WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);
									   WindowCloseEvent event;
									   data.EventCallback(event);
								   });

		glfwSetKeyCallback(m_Window, [](GLFWwindow *window, int key, int scancode, int action, int mods)
						   {
			WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);

			switch (action)
			{
				case GLFW_PRESS:
				{
					KeyPressedEvent event(key, 0);
					data.EventCallback(event);
					break;
				}
				case GLFW_RELEASE:
				{
					KeyReleasedEvent event(key);
					data.EventCallback(event);
					break;
				}
				case GLFW_REPEAT:
				{
					KeyPressedEvent event(key, true);
					data.EventCallback(event);
					break;
				}
			} });

		glfwSetCharCallback(m_Window, [](GLFWwindow *window, unsigned int keycode)
							{
								WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

								KeyTypedEvent event(keycode);
								data.EventCallback(event);
							});

		glfwSetMouseButtonCallback(m_Window, [](GLFWwindow *window, int button, int action, int mods)
								   {
			WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);

			switch (action)
			{
				case GLFW_PRESS:
				{
					MouseButtonPressedEvent event(button);
					data.EventCallback(event);
					break;
				}
				case GLFW_RELEASE:
				{
					MouseButtonReleasedEvent event(button);
					data.EventCallback(event);
					break;
				}
			} });

		glfwSetScrollCallback(m_Window, [](GLFWwindow *window, double xOffset, double yOffset)
							  {
								  WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

								  MouseScrolledEvent event((float)xOffset, (float)yOffset);
								  data.EventCallback(event);
							  });

		glfwSetCursorPosCallback(m_Window, [](GLFWwindow *window, double xPos, double yPos)
								 {
									 WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

									 MouseMovedEvent event((float)xPos, (float)yPos);
									 data.EventCallback(event);
								 });
	}

	void WindowsWindow::Shutdown()
	{
		glfwDestroyWindow(m_Window);
		--s_GLFWWindowCount;

		if (s_GLFWWindowCount == 0)
		{
			glfwTerminate();
		}
	}

	void WindowsWindow::OnUpdate()
	{
		if(m_Data.IsDesktop){
			QueryKeyAndMouse();
		}

		glfwPollEvents();
		m_Context->SwapBuffers();
	}

	void WindowsWindow::SetVSync(bool enabled)
	{
		if (enabled)
			glfwSwapInterval(1);
		else
			glfwSwapInterval(0);

		m_Data.VSync = enabled;
	}

	bool WindowsWindow::IsVSync() const
	{
		return m_Data.VSync;
	}

	void WindowsWindow::SetIsDesktopFlag(bool state)
	{
		m_Data.IsDesktop = state;
	}

    void WindowsWindow::SetFullScreen()
    {
        //fullscreen
        int monitorCount;
        GLFWmonitor** pMonitor = glfwGetMonitors(&monitorCount);

        int screen_x, screen_y;
        const GLFWvidmode * mode = glfwGetVideoMode(pMonitor[0]);
        screen_x = mode->width;
        screen_y = mode->height;

        glfwSetWindowSize(m_Window, screen_x, screen_y);
        glfwSetWindowPos(m_Window, 0, 0);
        CC_CORE_INFO("SetFullScreen w=%d,h=%d\n", screen_x, screen_y);
    }

    void WindowsWindow::SetNoTitleFullScreen()
    {
        //fullscreen
        int monitorCount;
        GLFWmonitor** pMonitor = glfwGetMonitors(&monitorCount);

        int screen_x, screen_y;
        const GLFWvidmode * mode = glfwGetVideoMode(pMonitor[0]);
        screen_x = mode->width;
        screen_y = mode->height + 20;

        glfwSetWindowSize(m_Window, screen_x, screen_y);
        glfwSetWindowPos(m_Window, 0, 0);
        CC_CORE_INFO("SetNoTitleFullScreen w=%d,h=%d\n", screen_x, screen_y);
    }

    void WindowsWindow::SetWindowTitle(std::string name) {
        glfwSetWindowTitle(m_Window, name.c_str());
    }

	void WINAPI WindowsWindow::QueryKeyAndMouse() {

		BYTE lpKeyState[256] = { 0 };

		SetKeyboardState(lpKeyState);

		{
			//        int MAX = 256;
			//        WCHAR p_name[256];

			GUITHREADINFO pg;
			pg.cbSize = sizeof(GUITHREADINFO);

			//        HWND hwnd = GetFocus();
            //HWND pHwnd = GetForegroundWindow();
            //DWORD tpId = GetWindowThreadProcessId(pHwnd, NULL);
            //
            //if (AttachThreadInput(GetCurrentThreadId(), tpId, TRUE)) {
            //	GetGUIThreadInfo(tpId, &pg);
            //
            //	if (pg.hwndCaret)
            //	{
            //		pointCaret.x = pg.rcCaret.right; /*rcCaret 光标*/
            //		pointCaret.y = pg.rcCaret.bottom;
            //		ClientToScreen(pg.hwndCaret, &pointCaret);
            //		//emit inputMousePointChanged(point);
            //	}
            //	else {
            //		pointCaret.x = pointCaret.y = 0;
            //		//emit inputMousePointChanged(point);
            //	}
            //}

			GetCursorPos(&pointMouse);
			//emit cursorPointChanged(point_mouse);
			{
				WindowData &data = *(WindowData *)glfwGetWindowUserPointer(m_Window);
				MouseMovedEvent event((float)pointMouse.x, (float)pointMouse.y);
				data.EventCallback(event);
			}			

            for (int i = 0; i < MAXKEYVALUESSET; i++) {
                if (0 == key_values_set[i].pressed && (GetAsyncKeyState(key_values_set[i].code) & 0x8000)) {
                    key_values_set[i].pressed = 1;
                    {
                        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(m_Window);
                        KeyWinEvent event(key_values_set[i].codeUser, true);
                        data.EventCallback(event);
                    }
                }
                //else if(1 == key_values_set[i].pressed){
                else if(1 == key_values_set[i].pressed && !(GetAsyncKeyState(key_values_set[i].code) & 0x8000)){
                    key_values_set[i].pressed = 0;
                    {
                        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(m_Window);
                        KeyWinEvent event(key_values_set[i].codeUser, false);
                        data.EventCallback(event);
                    }
                }
            }
		}
	}

}
